// it has only one requestAnimationFrame for all
// Handle all the loop drawcalls, intervals and timeouts
// Polyfills are handled separately.

function now() {
    return typeof performance == 'undefined' ? Date.now() : performance.now();
}

function RAF(autoStart) {
    const queue = [];
    let loop = null;
    let delayCount = 0;
    let startTime = 0;
    let oldTime = 0;
    let elapsedTime = 0;

    const animate = () => {
        // delta
        const currentTime = now();
        const delta = (currentTime - oldTime) / 1000;
        oldTime = currentTime;
        elapsedTime += delta;

        // loops
        let loopCount = 0;
        let timeouts = 0;
        let intervals = 0;

        if (queue.length > 0) {
            for (let i = 0; i < queue.length; i++) {
                const item = queue[i];

                // handle intervals
                if (item.isInterval) {
                    intervals++;
                    if (
                        item.time < 17 ||
                        currentTime - item.lastTime >= item.time
                    ) {
                        item.callback(elapsedTime, delta);
                        item.lastTime = currentTime;
                    }
                }
                // handle timeouts
                else if (item.isTimeout) {
                    timeouts++;
                    if (
                        item.time < 17 ||
                        currentTime - item.lastTime >= item.time
                    ) {
                        item.callback(elapsedTime, delta);
                        this.clearTimeout(item.id);
                    }
                }
                // handle subscribers
                else if (item.isLoop == 1) {
                    loopCount++;
                    item.callback(elapsedTime, delta);
                }
            }
        }

        // console.log( "All:", queue.length, "Loops", loopCount, "Intervals", intervals, "Timeouts", timeouts );
        loop = requestAnimationFrame(animate);
    };

    this.on = (subscriberId, cb) => {
        const item = { id: subscriberId, callback: cb, isLoop: 1 };
        queue.push(item);
    };

    this.off = (id) => {
        for (let i = 0; i < queue.length; i++) {
            if (id === queue[i].id) {
                let item = queue.splice(i, 1);
                delete item.callback;
                item = null;
                break;
            }
        }
    };

    this.setInterval = (cb, intervalTime) => {
        delayCount++;
        const intervalId = `rafInterval-${delayCount}`;
        const item = {
            id: intervalId,
            callback: cb,
            time: intervalTime,
            lastTime: now(),
            isInterval: true,
        };
        queue.push(item);
        return intervalId;
    };

    this.clearInterval = function (intervalId) {
        this.off(intervalId);
    };

    this.setTimeout = (cb, timeoutTime) => {
        delayCount++;
        const timeoutId = `rafTimeout-${delayCount}`;
        const item = {
            id: timeoutId,
            callback: cb,
            time: timeoutTime,
            lastTime: now(),
            isTimeout: true,
        };
        queue.push(item);
        return timeoutId;
    };

    this.clearTimeout = function (timeoutId) {
        this.off(timeoutId);
    };

    this.stop = () => {
        if (loop != null) {
            cancelAnimationFrame(loop);
            loop = null;
        }

        for (let i = 0; i < queue.length; i++) {
            const item = queue[i];
            if (item.isInterval || item.isTimeout) {
                delete queue[i].callback;
                queue[i] = null;
            }
        }

        delayCount = 0;
        startTime = now();
        oldTime = startTime;
        elapsedTime = 0;
    };

    this.start = function () {
        this.stop();
        animate();
    };

    autoStart != false && this.start();
}

export default RAF;
